#include "Server.h"
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <stdlib.h>
#include <dirent.h>
#include <ctype.h>

int32_t initListenFd(uint16_t port)
{
    // 1.创建监听的fd
    int32_t lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        perror("socket");
        return -1;
    }
    // 2.设置端口复用
    uint32_t opt = 1;
    int32_t ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret == -1)
    {
        perror("setsockopt");
        return -1;
    }
    // 3.绑定IP和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    ret = bind(lfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror("bind");
        return -1;
    }
    // 4.设置监听
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        perror("listen");
        return -1;
    }
    // 5.返回fd
    return lfd;
}

int32_t epollRun(int32_t lfd)
{
    int epfd = epoll_create(1);

    // 设置非阻塞
    int32_t flag = fcntl(lfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(lfd, F_SETFL, flag);

    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = lfd;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if (ret == -1)
    {
        perror("epoll_ctl");
    }
    struct epoll_event evs[1024];
    int32_t size = sizeof(evs) / sizeof(struct epoll_event);

    // 存储客户端信息
    int clientInfoFd = open("./clientInfo", O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if (clientInfoFd == -1)
    {
        perror("open clientInfo failed");
        return -1;
    }

    while (1)
    {
        int32_t num = epoll_wait(epfd, evs, size, -1);
        if (num == -1)
        {
            perror("epoll_wait");
            close(epfd);
            close(clientInfoFd);
            return -1;
        }
        printf("recv new connct\n");
        for (int32_t i = 0; i < num; i++)
        {
            fdInfo *info = (fdInfo *)malloc(sizeof(fdInfo));
            info->fd = evs[i].data.fd;
            info->epfd = epfd;
            info->clientInfoFd = clientInfoFd;

            if (info->fd == lfd)
            {
                // 建立新连接
                pthread_create(&info->tid, NULL, acceptClient, info);
                pthread_detach(info->tid);
            }
            else
            {
                // 主要是接受对端数据
                pthread_create(&info->tid, NULL, recvHttpRequest, info);
                pthread_detach(info->tid);
            }
        }
    }

    return 0;
}

void *acceptClient(void *arg)
{   
    fdInfo* info = (fdInfo*) arg;
    // 1.建立连接,存储客户端信息（可选）
    struct sockaddr_in cli_addr;
    socklen_t cli_len = 0;
    int32_t cfd = 0;
    while (1)
    {
        cli_len = sizeof(cli_addr); // 每次都重置
        cfd = accept(info->fd, (struct sockaddr *)&cli_addr, &cli_len);
        if (cfd == -1)
        {
            if (errno != EAGAIN && errno != EWOULDBLOCK)
            {
                perror("accept failed");
                return NULL;
            }
            printf("上树完毕\n");
            break;
        }
        printf("accept\n");
        // 获取客户端 IP 和端口
        char client_ip[INET_ADDRSTRLEN]; // 存储IP地址
        inet_ntop(AF_INET, &cli_addr.sin_addr, client_ip, sizeof(client_ip));
        int client_port = ntohs(cli_addr.sin_port);

        // 存储客户端信息
        char buffer[100];
        int len = snprintf(buffer, sizeof(buffer), "clientAddress:%s   clientPort:%d\n", client_ip, client_port);
        ssize_t written = 0, total_written = 0;
        while (total_written < len)
        {
            written = write(info->clientInfoFd, buffer + total_written, len - total_written);
            if (written == -1)
            {
                perror("write failed");
                return NULL;
            }
            total_written += written;
        }
        // 2.设置非阻塞
        int32_t flag = fcntl(cfd, F_GETFL);
        flag |= O_NONBLOCK;
        fcntl(cfd, F_SETFL, flag);

        // 3.cfd添加到epoll中
        struct epoll_event ev;
        ev.events = EPOLLIN | EPOLLET;
        ev.data.fd = cfd;
        int ret = epoll_ctl(info->epfd, EPOLL_CTL_ADD, cfd, &ev);
        if (ret == -1)
        {
            close(cfd);
            perror("acceptClient - epoll_ctl");
            return NULL;
        }
    }
    printf("acceptclient thread : %ld\n", info->tid);
    free(info);
    return NULL;
}

void * recvHttpRequest(void *arg)
{   
    fdInfo* info = (fdInfo*) arg;
    int32_t len = 0;
    char buff[4096] = {0};
    int32_t index = 0;
    while ((len = recv(info->fd, buff + index, sizeof(buff) - 1 - index, 0)) > 0)
    {
        index += len;
        if (index > sizeof(buff) - 1)
        {
            break;
        }
    }
    // 清空 cfd 缓冲区，丢弃后续数据
    char dump[1024];
    while (recv(info->fd, dump, sizeof(dump), MSG_DONTWAIT) > 0)
        ;
    printf("接收到数据:\n");
    printf("--------------\n");
    printf("%s", buff);
    printf("--------------\n");
    // 判断数据是否被接收完毕
    if (len == 0)
    {
        // 客户端断开了连接
        epoll_ctl(info->epfd, EPOLL_CTL_DEL, info->fd, NULL);
        close(info->fd);
    }
    else if (len == -1 || errno == EAGAIN)
    {
        // 解析请求行
        char *pt = strstr(buff, "\r\n");
        int reqLen = pt - buff;
        buff[reqLen] = '\0';
        parseRequestLine(buff, info->fd);
    }
    else
    {
        perror("recvHttpRequest recv error");
    }
    printf("recvHttpRequest thread : %ld\n", info->tid);
    free(info);
    return NULL;
}

int32_t parseRequestLine(const char *line, int cfd)
{
    // 解析请求行
    char method[12];
    char path[1024];
    sscanf(line, "%[^ ] %[^ ]", method, path);
    if (strcasecmp(method, "get") != 0)
    {
        return -1;
    }
    decodeMsg(path, path);
    // 处理客户端请求的静态资源（目录或者文件）
    char *file = NULL;
    if (strcmp(path, "/") == 0)
    {
        file = "./";
    }
    else
    {
        file = path + 1;
    }
    struct stat st;
    int ret = stat(file, &st);
    if (ret == -1)
    {
        printf("file not exist\n");
        // 文件不存在 -- 回复404
        const char *fileType = getFileType(".html");
        sendHeadMsg(cfd, 404, "Not Found", fileType, -1);
        sendFile("/home/sx-aozq/reactor_webServer/SimpleHttp/404.html", cfd);
        return 0;
    }
    // 判断文件类型
    if (S_ISDIR(st.st_mode))
    {
        // 把这个目录中的内容发送给客户端
        sendHeadMsg(cfd, 200, "OK", getFileType(".html"), st.st_size);
        sendDir(file, cfd);
    }
    else
    {
        // 把文件的内容发送给客户端
        const char *fileType = getFileType(file);
        sendHeadMsg(cfd, 200, "OK", fileType, st.st_size);
        sendFile(file, cfd);
    }

    return 0;
}

int32_t sendHeadMsg(int cfd, int status, const char *descr, const char *type, int32_t length)
{
    // 状态行
    char buff[4096] = {0};
    int offset = snprintf(buff, sizeof(buff), "HTTP/1.1 %d %s\r\n", status, descr);
    // 响应头
    offset += snprintf(buff + offset, sizeof(buff) - offset, "Content-Type: %s\r\n", type);
    offset += snprintf(buff + offset, sizeof(buff) - offset, "Content-Length: %d\r\n\r\n", length);

    send(cfd, buff, strlen(buff), 0);

    return 0;
}

int32_t sendFile(const char *fileName, int32_t cfd)
{
    int32_t fd = open(fileName, O_RDONLY);
    if (fd < 0)
    {
        perror("open error");
    }
    assert(fd > 0);
#if 0
    char buff[1024] = {0};
    int32_t len = 0;
    while ((len = read(fd, buff, sizeof(buff)))> 0)
    {
        send(cfd, buff, len, 0);
        memset(buff, 0, sizeof(buff));
        usleep(10);
    }
    if(len == -1)
    {
        perror("read error");
    }
#else
    int64_t size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET); // 重要！让文件指针回到开头
    off_t offset = 0;
    int64_t remaining = size;

    while (remaining > 0)
    {
        ssize_t sent = sendfile(cfd, fd, &offset, remaining);
        if (sent == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 等待 socket 可写再继续发送
                printf("无数据\n");
                continue;
            }
            perror("sendfile error");
            break;
        }
        remaining -= sent;
    }

#endif
    return 0;
}

const char *getFileType(const char *fileName)
{
    const char *dot = strrchr(fileName, '.');

    if (dot == NULL)
        return "text/plain; charset=utf-8"; // 纯文本
    if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
        return "text/html; charset=utf-8";
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
        return "image/jpeg";
    if (strcmp(dot, ".gif") == 0)
        return "image/gif";
    if (strcmp(dot, ".png") == 0)
        return "image/png";
    if (strcmp(dot, ".css") == 0)
        return "text/css";
    if (strcmp(dot, ".au") == 0)
        return "audio/basic";
    if (strcmp(dot, ".wav") == 0)
        return "audio/wav";
    if (strcmp(dot, ".avi") == 0)
        return "video/x-msvideo";
    if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
        return "video/quicktime";
    if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
        return "video/mpeg";
    if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
        return "model/vrml";
    if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
        return "audio/midi";
    if (strcmp(dot, ".mp3") == 0)
        return "audio/mpeg";
    if (strcmp(dot, ".ogg") == 0)
        return "application/ogg";
    if (strcmp(dot, ".pac") == 0)
        return "application/x-ns-proxy-autoconfig";

    return "text/plain; charset=utf-8";
}

int32_t sendDir(const char *dirName, int32_t cfd)
{
    char buff[4096] = {0};
    snprintf(buff, sizeof(buff), "<html><head><title>%s</title></head><body><table>", dirName);
    struct dirent **nameList = NULL;
    int num = scandir(dirName, &nameList, NULL, alphasort);
    for (int i = 0; i < num; ++i)
    {
        // 取出文件名
        char *name = nameList[i]->d_name;
        struct stat st;
        char subPath[1024] = {0};
        snprintf(subPath, sizeof(subPath), "%s/%s", dirName, name);
        stat(subPath, &st);
        if (S_ISDIR(st.st_mode))
        {
            // a标签 <a href="">name</a>
            snprintf(buff + strlen(buff), sizeof(buff), "<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",
                     name, name, st.st_size);
        }
        else
        {
            snprintf(buff + strlen(buff), sizeof(buff), "<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",
                     name, name, st.st_size);
        }
        send(cfd, buff, strlen(buff), 0);
        memset(buff, 0, sizeof(buff));
        free(nameList[i]);
    }
    snprintf(buff, strlen(buff), "</table></body></html>");
    send(cfd, buff, strlen(buff), 0);
    free(nameList);

    return 0;
}

// 将字符转换为整形数
int hexToDec(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;

    return 0;
}

// 解码
// to 存储解码之后的数据, 传出参数, from被解码的数据, 传入参数
void decodeMsg(char *to, char *from)
{
    for (; *from != '\0'; ++to, ++from)
    {
        // isxdigit -> 判断字符是不是16进制格式, 取值在 0-f
        // Linux%E5%86%85%E6%A0%B8.jpg
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
        {
            // 将16进制的数 -> 十进制 将这个数值赋值给了字符 int -> char
            // B2 == 178
            // 将3个字符, 变成了一个字符, 这个字符就是原始数据
            *to = hexToDec(from[1]) * 16 + hexToDec(from[2]);

            // 跳过 from[1] 和 from[2] 因此在当前循环中已经处理过了
            from += 2;
        }
        else
        {
            // 字符拷贝, 赋值
            *to = *from;
        }
    }
    *to = '\0';
}
